#include "Shader.h"
#include "..\base\Engine.h"
#include "..\utils\Log.h"
#include "..\renderer\Camera.h"

namespace ds {

Shader::Shader(const char* handle,const std::string& fileName,const std::string& technique) 
	: Asset(handle), m_FileName(fileName),m_Technique(technique) , m_Active(true) {	
}

Shader::~Shader(void) {
	SAFE_RELEASE(m_FX);
}

void Shader::init() {
	// Create the FX from a .fx file.
	LOGC(logINFO,"Shader") << "Init " << m_EffectName;
	ID3DXBuffer* errors = 0;
	HRESULT hr = D3DXCreateEffectFromFileA(gEngine->getDevice(), m_FileName.c_str(),0, 0, D3DXSHADER_DEBUG, 0, &m_FX, &errors);
	if ( hr != S_OK && errors != 0 ) {
		LOGC(logERROR,"Shader") << "Error while loading shader: " << (char*)errors->GetBufferPointer();
	}
	m_hTech = m_FX->GetTechniqueByName(m_Technique.c_str());
	
	D3DXEFFECT_DESC effectDesc;
	m_FX->GetDesc(&effectDesc);
	for ( UINT i = 0; i < effectDesc.Parameters;++i ) {
		D3DXHANDLE hParam = m_FX->GetParameter( NULL, i );
		D3DXPARAMETER_DESC pDesc;
		// get parameter description
		m_FX->GetParameterDesc( hParam, &pDesc );
		LOGC(logINFO,"Shader") << "Parameter : " << pDesc.Name << " Type: " << pDesc.Type;
		m_Handles[pDesc.Name] = hParam;
	}
}

bool Shader::reload() {
	ID3DXEffect* tmpEffect;
	LOGC(logINFO,"Shader") << "Reloading " << m_EffectName;
	ID3DXBuffer* errors = 0;
	HRESULT hr = D3DXCreateEffectFromFileA(gEngine->getDevice(), m_FileName.c_str(),0, 0, D3DXSHADER_DEBUG, 0, &tmpEffect, &errors);
	if ( hr != S_OK && errors != 0 ) {
		LOGC(logERROR,"Shader") << "Error while loading shader: " << (char*)errors->GetBufferPointer();
		LOGC(logERROR,"Shader") << "-- not reloading shader";
		return false;
	}
	SAFE_RELEASE(tmpEffect);
	SAFE_RELEASE(m_FX);
	init();
	return true;
}

void Shader::setTechnique() {
	m_FX->SetTechnique(m_hTech);
	D3DXHANDLE hndl = m_FX->GetParameterByName(0,"gWVP");
	if ( hndl != NULL ) {
		m_FX->SetMatrix(hndl,&(gEngine->getRenderer()->getWVPMatrix()));
	}
	hndl = m_FX->GetParameterByName(0,"World");
	if ( hndl != NULL ) {
		m_FX->SetMatrix(hndl,&(gEngine->getRenderer()->getWorldMatrix()));
	}
}

void Shader::setColor(const std::string& handle,const Color& c) {
	if ( m_Handles.find(handle) != m_Handles.end() ) {
		m_FX->SetValue(m_Handles[handle],&c,sizeof(D3DXCOLOR));
	}
}

void Shader::setFloat(const std::string& handle,float c) {
	if ( m_Handles.find(handle) != m_Handles.end() ) {
		m_FX->SetFloat(m_Handles[handle],c);
	}
}

void Shader::setVec3(const std::string& handle,const Vec3& v) {
	if ( m_Handles.find(handle) != m_Handles.end() ) {
		m_FX->SetValue(m_Handles[handle],&v,sizeof(Vec3));
	}
}

void Shader::setTexture(const std::string& handle,Texture* texture) {
	if ( m_Handles.find(handle) != m_Handles.end() ) {
		m_FX->SetTexture(m_Handles[handle],texture->texture);
	}
}

ID3DXEffect* Shader::getFX() {
	return m_FX;
}

// ------------------------------------------------------------------
//
// ------------------------------------------------------------------
BasicShader::BasicShader(const char* handle) : Shader(handle,"data\\effects\\basic.fx","BasicTechnique") {
}

void BasicShader::setTexture(const char* textureName) {
	ResourceHandle rh = gEngine->getResourceManager().findByName(textureName,RS_TEXTURE);
	Texture* t = gEngine->getResourceManager().getTexture(rh);
	m_FX->SetTexture(m_Handles["gTex"],t->texture);
	m_FX->SetBool(m_Handles["TextureEnabled"],true);
}

void BasicShader::setDiffuseColor(const Color& color) {
	m_FX->SetValue(m_Handles["DiffuseColor"],&color,sizeof(D3DXCOLOR));
}

void BasicShader::setAmbientLightColor(const Color& color) {
	m_FX->SetValue(m_Handles["AmbientLightColor"],&color,sizeof(D3DXCOLOR));
}

void BasicShader::setLightColor(const Color& color) {
	m_FX->SetValue(m_Handles["LightColor"],&color,sizeof(D3DXCOLOR));
}

void BasicShader::setLightDirection(const Vec3& lightDir){
	m_FX->SetValue(m_Handles["LightDirection"],&lightDir,sizeof(Vec3));
}

void BasicShader::setCameraPosition(const Vec3& cameraPos) {
	m_FX->SetValue(m_Handles["CameraPosition"],&cameraPos,sizeof(Vec3));
}

ColorShader::ColorShader(const char* handle,const Color& color) 	: Shader(handle), m_Color(color) {	
	setActive(true);
}

void ColorShader::init() {	
	ID3DXBuffer* errors = 0;
	const char* g_strBuffer = 
		"uniform extern float4x4 gWVP;\r\n"
		"float4 color;\r\n"
		"struct OutputVS {\r\n"
		"	float4 posH   : POSITION0;\r\n"
		"	float2 tex0   : TEXCOORD0;\r\n"
		"};\r\n"
		"OutputVS ColorVS(float4 posL : POSITION0,float2 tex0: TEXCOORD0) {\r\n"
		"	OutputVS outVS = (OutputVS)0;\r\n"
		"	outVS.posH = posL;\r\n"
		"	outVS.tex0 = tex0; \r\n"
		"	return outVS;\r\n"
		"}\r\n"
		"float4 ColorPS(float2 tex0 : TEXCOORD0) : COLOR {\r\n"
		"	return color;\r\n"
		"}\r\n"
		"technique ColorTech {\r\n"
		"	pass P0 {\r\n"
		"		vertexShader = compile vs_2_0 ColorVS();\r\n"
		"		pixelShader  = compile ps_2_0 ColorPS();\r\n"
		"	}\r\n"
		"}\r\n";
	UINT dwBufferSize = ( UINT )strlen( g_strBuffer ) + 1;
	HR(D3DXCreateEffect(gEngine->getDevice(), g_strBuffer, dwBufferSize,0, 0, D3DXSHADER_DEBUG, 0, &m_FX, &errors));
	m_hTech = m_FX->GetTechniqueByName("ColorTech");
	D3DXEFFECT_DESC effectDesc;
	m_FX->GetDesc(&effectDesc);
	for ( UINT i = 0; i < effectDesc.Parameters;++i ) {
		D3DXHANDLE hParam = m_FX->GetParameter( NULL, i );
		D3DXPARAMETER_DESC pDesc;
		// get parameter description
		m_FX->GetParameterDesc( hParam, &pDesc );
		m_Handles[pDesc.Name] = hParam;
	}
	Shader::setColor("color",m_Color);
}


TransformedTextureShader::TransformedTextureShader(const char* handle,const std::string& textureName)
	: Shader(handle) ,m_TextureName(textureName) {	
	setActive(true);
}

void TransformedTextureShader::init() {	
	ID3DXBuffer* errors = 0;
	const char* g_strBuffer = 
		"uniform extern float4x4 gWVP;\r\n"
		"uniform extern texture gTex;\r\n"
		"\r\n"
	"sampler TexS = sampler_state {\r\n"
	"	Texture = <gTex>;\r\n"
	"	MinFilter = POINT;\r\n"
	"	MagFilter = LINEAR;\r\n"
	"	MipFilter = LINEAR;\r\n"
	"	AddressU  = WRAP;\r\n"
	"	AddressV  = WRAP;\r\n"
	"};\r\n"
	"struct OutputVS {\r\n"
	"	float4 posH   : POSITION0;\r\n"
	"	float2 tex0   : TEXCOORD0;\r\n"
	"};\r\n"
	"OutputVS TextureVS(float4 posL : POSITION0,float2 tex0: TEXCOORD0) {\r\n"
	"	OutputVS outVS = (OutputVS)0;	\r\n"
	"	outVS.posH = posL;\r\n"
	"	outVS.tex0 = tex0; \r\n"
	"	return outVS;\r\n"
	"}\r\n"
	"float4 TexturePS(float2 tex0 : TEXCOORD0) : COLOR {    \r\n"
	"	return  tex2D(TexS, tex0);\r\n"
	"}\r\n"
	"technique TextureTech {\r\n"
	"	pass P0 {\r\n"
	"		vertexShader = compile vs_2_0 TextureVS();\r\n"
	"		pixelShader  = compile ps_2_0 TexturePS();\r\n"
	"	}\r\n"
	"}\r\n";
	UINT dwBufferSize = ( UINT )strlen( g_strBuffer ) + 1;
	HR(D3DXCreateEffect(gEngine->getDevice(), g_strBuffer, dwBufferSize,0, 0, D3DXSHADER_DEBUG, 0, &m_FX, &errors));
	m_hTech = m_FX->GetTechniqueByName("TextureTech");
	D3DXEFFECT_DESC effectDesc;
	m_FX->GetDesc(&effectDesc);
	for ( UINT i = 0; i < effectDesc.Parameters;++i ) {
		D3DXHANDLE hParam = m_FX->GetParameter( NULL, i );
		D3DXPARAMETER_DESC pDesc;
		// get parameter description
		m_FX->GetParameterDesc( hParam, &pDesc );
		m_Handles[pDesc.Name] = hParam;
	}
	ResourceHandle rh = gEngine->getResourceManager().findByName(m_TextureName.c_str(),RS_TEXTURE);
	Texture* t = gEngine->getResourceManager().getTexture(rh);
	Shader::setTexture("gTex",t);
}

TransformedTextureColorShader::TransformedTextureColorShader(const char* handle,const std::string& textureName)
	: Shader(handle) ,m_TextureName(textureName) {	
		setActive(true);
}

void TransformedTextureColorShader::init() {	
	ID3DXBuffer* errors = 0;
	const char* g_strBuffer = 
		"float4x4 gWVP;\r\n"
		"texture gTex;\r\n"
		"sampler TexS = sampler_state {\r\n"
		"Texture = <gTex>;\r\n"
		"MinFilter = LINEAR;\r\n"
		"MagFilter = LINEAR;\r\n"
		"MipFilter = LINEAR;\r\n"
		"AddressU  = WRAP;\r\n"
		"AddressV  = WRAP;\r\n"
		"};\r\n"
		"struct OutputVS {\r\n"
		"float4 posH   : POSITION0;\r\n"
		"float2 tex0   : TEXCOORD0;\r\n"
		"float4 color  : COLOR0;\r\n"
		"};\r\n"
		"OutputVS TTCVVS(float4 posL : POSITION0, float2 tex0: TEXCOORD0,float4 color : COLOR0) {\r\n"
		"OutputVS outVS = (OutputVS)0;\r\n"
		"outVS.posH = posL;\r\n"
		"outVS.tex0 = tex0; \r\n"
		"outVS.color = color;\r\n"
		"return outVS;\r\n"
		"}\r\n"
		"float4 TTCVPS(float2 texCoord : TEXCOORD0,float4 color : COLOR0) : COLOR {\r\n"
		"float4 t = tex2D(TexS, texCoord);\r\n"
		"return color * t;     \r\n"
		"}\r\n"
		"technique TTCVTech {\r\n"
		"pass P0 {\r\n"
		"vertexShader = compile vs_2_0 TTCVVS();\r\n"
		"pixelShader  = compile ps_2_0 TTCVPS();\r\n"
		"}\r\n"
		"}\r\n";
	UINT dwBufferSize = ( UINT )strlen( g_strBuffer ) + 1;
	HR(D3DXCreateEffect(gEngine->getDevice(), g_strBuffer, dwBufferSize,0, 0, D3DXSHADER_DEBUG, 0, &m_FX, &errors));
	m_hTech = m_FX->GetTechniqueByName("TTCVTech");
	D3DXEFFECT_DESC effectDesc;
	m_FX->GetDesc(&effectDesc);
	for ( UINT i = 0; i < effectDesc.Parameters;++i ) {
		D3DXHANDLE hParam = m_FX->GetParameter( NULL, i );
		D3DXPARAMETER_DESC pDesc;
		// get parameter description
		m_FX->GetParameterDesc( hParam, &pDesc );
		m_Handles[pDesc.Name] = hParam;
	}
	ResourceHandle rh = gEngine->getResourceManager().findByName(m_TextureName.c_str(),RS_TEXTURE);
	Texture* t = gEngine->getResourceManager().getTexture(rh);
	assert( t != 0 );
	Shader::setTexture("gTex",t);
}

}
